YouTubeChatIframe.tsx 1.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. 'use client';
  2. import { useEffect, useState } from 'react';
  3. import './youtube-chat-iframe.scss';
  4. type Props = {
  5. videoId: string|null;
  6. };
  7. /**
  8. * YouTube Live Chat 임베드 컴포넌트.
  9. *
  10. * - URL 형식: https://www.youtube.com/live_chat?v={VIDEO_ID}&embed_domain={HOST}
  11. * - embed_domain 은 NEXT_PUBLIC_EMBED_DOMAIN 환경변수 사용 (호스트만, 프로토콜/포트 제외)
  12. * - 라이브 중일 때만 정상 표시 (videoId 가 있어야 함)
  13. * - 비라이브 또는 환경변수 미설정 시 fallback UI
  14. *
  15. * Quota 비용: 0 — YouTube 자체 인프라 사용, antooza 의 quota 소비 없음.
  16. */
  17. export default function YouTubeChatIframe({ videoId }: Props)
  18. {
  19. const [embedDomain, setEmbedDomain] = useState<string|null>(null);
  20. useEffect(() => {
  21. // SSR 시점에는 process.env 가 없을 수 있어 마운트 후 해석
  22. const envDomain = process.env.NEXT_PUBLIC_EMBED_DOMAIN;
  23. if (envDomain) {
  24. setEmbedDomain(envDomain);
  25. return;
  26. }
  27. // fallback: 현재 호스트 (개발 환경 보조)
  28. if (typeof window !== 'undefined') {
  29. setEmbedDomain(window.location.hostname);
  30. }
  31. }, []);
  32. const chatUrl = videoId && embedDomain
  33. ? `https://www.youtube.com/live_chat?v=${encodeURIComponent(videoId)}&embed_domain=${encodeURIComponent(embedDomain)}`
  34. : null;
  35. return (
  36. <div className="yt-chat">
  37. {chatUrl ? (
  38. <iframe
  39. className="yt-chat__iframe"
  40. src={chatUrl}
  41. title="YouTube Live Chat"
  42. allow="autoplay"
  43. sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-forms"
  44. />
  45. ) : (
  46. <div className="yt-chat__offline">
  47. <p className="yt-chat__offline-title">현재 라이브 방송이 아닙니다</p>
  48. <p className="yt-chat__offline-desc">라이브 방송이 시작되면 채팅에 참여할 수 있어요.</p>
  49. </div>
  50. )}
  51. </div>
  52. );
  53. }